package com.pt.pok.string.legacy;

import com.playtech.common.Constants;
import com.playtech.common.network.GenericCommand;

import java.io.UTFDataFormatException;
import java.io.UnsupportedEncodingException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.playtech.common.Constants.CHS;

/**
 * This is a copy-paste from Poker Channel
 */
public class UTFStringCommand
    extends GenericCommand {

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    /**
     * When command is built this field is initialized and whenever rebuilt then this value is used instead of
     * generating the string again
     */
    private String builtString = null;

    /**
     * @param commandCode
     * @param errorCode
     * @param parameters
     */
    public UTFStringCommand(int commandCode, short errorCode, List<Object> parameters) {
        super(commandCode, (byte) 0, Constants.ComponentType.LOBBY, 0L, Constants.ComponentType.LOBBY, new long[] {0L}, errorCode, parameters);
    }

    public UTFStringCommand(int commandCode, short errorCode, Object... parameters) {
        super(commandCode,
              (byte) 0,
              Constants.ComponentType.LOBBY,
              0L,
              Constants.ComponentType.LOBBY,
              new long[] {0L},
              errorCode,
              parameters == null ? Collections.emptyList() : Arrays.asList(parameters));
    }

    public UTFStringCommand() {}

    @Override
    public ByteBuffer build() {
        try {
            String buf = getBuiltString();

            return ByteBuffer.wrap(buf.getBytes(Constants.ISO_8859_1));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    // Request 51765, task 106172 (radoslava)
    /**
     * @return
     * @throws java.io.UnsupportedEncodingException
     */
    protected synchronized String getBuiltString()
        throws UnsupportedEncodingException {
        if (builtString != null)
            return builtString;

        StringBuilder buf = new StringBuilder(256);
        buf.append(getCode()).append(CHS);
        buf.append(getErrorCode());
        for (Object param : getParameters()) {
            buf.append(CHS);
            buf.append(param);
        }

        builtString = getUTF8String(buf.toString(), CHS);

        return builtString;
    }

    public static String getUTF8String(String src)
        throws UnsupportedEncodingException {
        return getUTF8String(src, (char[]) null);
    }

    public static String getUTF8String(String src, char excludeChars)
        throws UnsupportedEncodingException {
        return getUTF8String(src, new char[] {excludeChars});
    }

    public static String getUTF8String(String src, char[] excludeChars)
        throws UnsupportedEncodingException {
        int len = src.length();
        char[] chars = src.toCharArray();
        byte[] dest = new byte[len * 3];
        int utflen = 0;
        next: for (int cpos = 0; cpos < len; cpos++) {
            char c = chars[cpos];

            for (int j = 0; excludeChars != null && j < excludeChars.length; j++) {
                if (c == excludeChars[j]) {
                    dest[utflen++] = ((byte) c);
                    continue next;
                }
            }

            if (c <= 0x007F && c != 0) {
                dest[utflen++] = ((byte) c);
            } else if (c > 0x07FF) {
                dest[utflen++] = ((byte) (0xE0 | ((c >> 12) & 0x0F)));
                dest[utflen++] = ((byte) (0x80 | ((c >> 6) & 0x3F)));
                dest[utflen++] = ((byte) (0x80 | ((c >> 0) & 0x3F)));
            } else {
                dest[utflen++] = ((byte) (0xC0 | ((c >> 6) & 0x1F)));
                dest[utflen++] = ((byte) (0x80 | ((c >> 0) & 0x3F)));
            }
        }

        return new String(dest, 0, utflen, Constants.ISO_8859_1);
    }

    public static String fromUTF8String(String src)
        throws UnsupportedEncodingException, UTFDataFormatException {
        return fromUTF8String(src, (char[]) null);
    }

    public static String fromUTF8String(String src, char excludeChars)
        throws UnsupportedEncodingException, UTFDataFormatException {
        return fromUTF8String(src, new char[] {excludeChars});
    }

    public static String fromUTF8String(String src, char[] excludeChars)
        throws UnsupportedEncodingException, UTFDataFormatException {
        char[] chars = src.toCharArray();
        int pos = 0;
        next: for (int i = 0; i < chars.length;) {

            for (int j = 0; excludeChars != null && j < excludeChars.length; j++) {
                if (chars[i] == excludeChars[j]) {
                    chars[pos++] = (chars[i++]);
                    continue next;
                }
            }

            int b1, b2, b3;
            b1 = chars[i++] & 0xFF;
            switch (b1 >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7: // 1 byte format: 0xxxxxxx
                    chars[pos++] = ((char) b1);
                    break;

                case 12:
                case 13: // 2 byte format: 110xxxxx 10xxxxxx
                    b2 = chars[i++];
                    if ((b2 & 0xC0) != 0x80) {
                        throw new UTFDataFormatException("At pos " + (i - 1) + " in " + src);
                    }
                    chars[pos++] = ((char) (((b1 & 0x1F) << 6) | ((b2 & 0x3F) << 0)));
                    break;

                case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
                    b2 = chars[i++];
                    b3 = chars[i++];
                    if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
                        throw new UTFDataFormatException("At pos " + (i - 2) + " or " + (i - 1) + " in " + src);
                    }
                    chars[pos++] = ((char) (((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | ((b3 & 0x3F) << 0)));
                    break;
            }
        }

        return new String(chars, 0, pos);
    }

    @Override
    public void load(ByteBuffer buf)
        throws IllegalArgumentException, BufferUnderflowException {

    }

    @Override
    public String toString() {
        try {
            return getBuiltString();
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }

        return "";
    }

    public static void main(String[] args) {
        try {
            // String s = getUTF8String("\u0394");
            // for (int i = 0; i < s.length(); i++) {
            // char c = s.charAt(i);
            // System.out.println(c + " " + (int)c);
            // }
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 300000; i++) {
                sb.append("a");
            }
            char[] exclude = new char[] {CHS};
            String s = sb.toString();
            int count = 10000;
            long start = System.nanoTime();
            for (int i = 0; i < count; i++) {
                getUTF8String(s, exclude);
            }
            long end = System.nanoTime();
            // count = 10000;
            System.out.println("Avg time for " + s.length() + " characters = " + (((end - start) / (1D * count)) / 1000000) + "ms");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
    }
}
